#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os, re, time, base64, datetime, traceback
from collections import defaultdict, Counter
import shutil

import fitz
import anthropic
import google.generativeai as genai
import openpyxl
from openpyxl.styles import PatternFill, Font, Alignment

# ═══════════════════════════════════════════════════════════
#  BEÁLLÍTÁSOK
# ═══════════════════════════════════════════════════════════

CLAUDE_API_KEY       = "BLINDED_PLACEHOLDER"
GEMINI_API_KEY       = "BLINDED_PLACEHOLDER"

PDF_MAPPA            = "pdf_cikkek"
PROMPT_FAJL          = "prompt.txt"
SABLON_FAJL          = "data_extraction.xlsx"
FUTTATÁSOK_SZAMA     = 3

CLAUDE_AKTIV         = True
GEMINI_AKTIV         = True

CLAUDE_MODEL         = "claude-sonnet-4-6"
GEMINI_MODEL         = "gemini-3.1-pro-preview"

MAX_UJRAPROBALKOZAS  = 3   # API hibák esetén (rate limit stb.)
MAX_JAVITAS          = 2   # 87-érték retry
VARAKOZAS_MASODPERC  = 15

# Megbízhatósági küszöbök
ZOLD_KUSZOB  = 5   # 5–6/6 → zöld
SARGA_KUSZOB = 3   # 3–4/6 → sárga  (≤2 → piros)

# ═══════════════════════════════════════════════════════════
#  MEZŐTÍPUSOK (0-indexelt, 87 mező)
# ═══════════════════════════════════════════════════════════
# SZAM: pontos szám egyezés számít
# KAT:  kategória egyezés (normalizáltan)
# SZOV: szabad szöveg — a legjobb futtatásból vesszük

MEZO_TIPUSOK = {
    # Sorszám (0-indexelt): típus
    # IDENTIFICATION
    0: 'SZOV',   # Summary
    1: 'KAT',    # Research domain
    2: 'SZAM',   # N outcomes
    3: 'SZOV',   # Authors
    4: 'SZAM',   # Year
    5: 'KAT',    # Country
    6: 'KAT',    # Language
    7: 'KAT',    # Source DB
    8: 'KAT',    # DOI
    9: 'KAT',    # Funding
    10: 'KAT',   # Affiliation
    11: 'KAT',   # Study design
    12: 'KAT',   # Stat model type
    13: 'SZAM',  # Sample size
    14: 'SZOV',  # Age/Grade
    15: 'SZAM',  # Gender %F
    16: 'SZOV',  # SES
    17: 'KAT',   # Waldorf type
    18: 'SZAM',  # Exposure yrs
    19: 'SZOV',  # Waldorf methods
    20: 'KAT',   # School level
    21: 'KAT',   # Follow-up
    22: 'KAT',   # Control type
    23: 'KAT',   # Control matching
    24: 'SZAM',  # Attrition
    25: 'SZOV',  # Waldorf desc
    26: 'SZOV',  # Control desc
    27: 'SZOV',  # Intervention detail
    28: 'SZOV',  # Comparison detail
    29: 'SZOV',  # Blinding
    30: 'SZOV',  # Stat method
    31: 'SZOV',  # ES method
    32: 'SZOV',  # Missing data
    33: 'SZOV',  # Other notes
    34: 'SZOV',  # Controlled vars
    # OUTCOMES
    35: 'KAT',   # O1 category
    36: 'SZOV',  # O1 outcome
    37: 'SZOV',  # O1 instrument
    38: 'KAT',   # O1 validation
    39: 'KAT',   # O1 timepoints
    40: 'KAT',   # O2 category
    41: 'SZOV',  # O2 outcome
    42: 'SZOV',  # O2 instrument
    43: 'KAT',   # O2 validation
    44: 'KAT',   # O2 timepoints
    45: 'KAT',   # O3 category
    46: 'SZOV',  # O3 outcome
    47: 'SZOV',  # O3 instrument
    48: 'KAT',   # O3 validation
    49: 'KAT',   # O3 timepoints
    50: 'SZOV',  # O4+
    51: 'SZOV',  # Conf vars detail
    52: 'SZOV',  # Stat method detail
    53: 'SZOV',  # ES method used
    54: 'SZOV',  # Missing detail
    55: 'SZOV',  # Other notes 2
    # RESULTS O1
    56: 'SZAM',  # O1 W_M
    57: 'SZAM',  # O1 W_SD
    58: 'SZAM',  # O1 W_n
    59: 'SZAM',  # O1 C_M
    60: 'SZAM',  # O1 C_SD
    61: 'SZAM',  # O1 C_n
    62: 'SZOV',  # O1 test stat
    63: 'SZAM',  # O1 alt ES value
    64: 'KAT',   # O1 alt ES type
    65: 'SZAM',  # O1 ES
    66: 'KAT',   # O1 ES type
    67: 'SZOV',  # O1 CI
    68: 'SZAM',  # O1 p
    69: 'KAT',   # O1 direction
    70: 'SZOV',  # O1 notes
    # RESULTS O2
    71: 'SZAM',  # O2 W_M
    72: 'SZAM',  # O2 W_SD
    73: 'SZAM',  # O2 W_n
    74: 'SZAM',  # O2 C_M
    75: 'SZAM',  # O2 C_SD
    76: 'SZAM',  # O2 C_n
    77: 'SZOV',  # O2 test stat
    78: 'SZAM',  # O2 alt ES value
    79: 'KAT',   # O2 alt ES type
    80: 'SZAM',  # O2 ES
    81: 'KAT',   # O2 ES type
    82: 'SZOV',  # O2 CI
    83: 'SZAM',  # O2 p
    84: 'KAT',   # O2 direction
    85: 'SZOV',  # O2 notes
    86: 'SZOV',  # Reviewer notes
}

# ═══════════════════════════════════════════════════════════
#  SZÍNEK
# ═══════════════════════════════════════════════════════════

FILL_ZOLD    = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
FILL_SARGA   = PatternFill(start_color="FFFF99", end_color="FFFF99", fill_type="solid")
FILL_PIROS   = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
FILL_NARANCS = PatternFill(start_color="FFD966", end_color="FFD966", fill_type="solid")
FILL_FEJLEC  = PatternFill(start_color="2F5496", end_color="2F5496", fill_type="solid")
FEHER_FONT   = Font(bold=True, color="FFFFFF")
FELKOVER     = Font(bold=True)

BIAS_TIPUS = {17: 'ROBINS-I', 13: 'RoB 2', 18: 'NOS', 14: 'CASP'}

# ═══════════════════════════════════════════════════════════
#  API KLIENSEK
# ═══════════════════════════════════════════════════════════

claude_client    = anthropic.Anthropic(api_key=CLAUDE_API_KEY)
genai.configure(api_key=GEMINI_API_KEY)
gemini_model_obj = genai.GenerativeModel(GEMINI_MODEL)

gemini_sikertelen_pdf = []  # Szkennelt PDF-ek listája

# ═══════════════════════════════════════════════════════════
#  PDF KEZELÉS
# ═══════════════════════════════════════════════════════════

def pdf_to_base64(pdf_path):
    with open(pdf_path, "rb") as f:
        return base64.standard_b64encode(f.read()).decode("utf-8")

def pdf_to_text(pdf_path):
    szoveg = ""
    try:
        doc = fitz.open(pdf_path)
        for oldal in doc:
            szoveg += oldal.get_text()
        doc.close()
    except Exception as e:
        print(f"  ⚠️  PDF szöveg hiba: {e}")
    return szoveg

# ═══════════════════════════════════════════════════════════
#  API HÍVÁSOK + RETRY
# ═══════════════════════════════════════════════════════════

def claude_hivas(prompt, pdf_path):
    valasz = claude_client.messages.create(
        model=CLAUDE_MODEL, max_tokens=8000, temperature=0.0,
        messages=[{"role": "user", "content": [
            {"type": "document", "source": {
                "type": "base64", "media_type": "application/pdf",
                "data": pdf_to_base64(pdf_path)}},
            {"type": "text", "text": prompt}]}])
    return "".join(b.text for b in valasz.content if hasattr(b, "text"))

def gemini_hivas(prompt, pdf_path):
    szoveg = pdf_to_text(pdf_path)
    if not szoveg.strip():
        fajlnev = os.path.basename(pdf_path)
        if fajlnev not in gemini_sikertelen_pdf:
            gemini_sikertelen_pdf.append(fajlnev)
        raise ValueError(f"Szkennelt PDF — Gemini nem olvassa: {fajlnev}")
    valasz = gemini_model_obj.generate_content(
        f"{prompt}\n\n--- TANULMÁNY ---\n\n{szoveg}",
        generation_config=genai.types.GenerationConfig(temperature=0.0))
    return valasz.text

def api_ujraprobalkozassal(api_fn, prompt, pdf_path):
    for i in range(1, MAX_UJRAPROBALKOZAS + 1):
        try:
            return api_fn(prompt, pdf_path)
        except ValueError:
            raise
        except Exception as e:
            if i < MAX_UJRAPROBALKOZAS:
                print(f"\n    ⚠️  Hiba ({i}. próba): {str(e)[:80]}")
                print(f"    ⏳ Várakozás {VARAKOZAS_MASODPERC} mp...")
                time.sleep(VARAKOZAS_MASODPERC)
            else:
                raise

JAVITO_PROMPT_SABLON = """Az előző válaszodban az adatkinyerési TSV blokkban {kapott} értéket adtál 87 helyett.

KÖTELEZŐ JAVÍTÁS: Küldd vissza CSAK az adatkinyerési TSV blokkot, pontosan 87 tabulátorral elválasztott értékkel egy sorban.
- Minden ismeretlen mező: "NR"
- Minden nem releváns mező: "N/A"
- Nincs sortörés a soron belül
- Nincs fejléc, nincs magyarázat — CSAK a TSV blokk:

```
érték1\térték2\t...\térték87
```"""

def claude_javitas(eredeti_valasz, kapott_n, pdf_path):
    javito = JAVITO_PROMPT_SABLON.format(kapott=kapott_n)
    valasz = claude_client.messages.create(
        model=CLAUDE_MODEL, max_tokens=4000, temperature=0.0,
        messages=[
            {"role": "user", "content": [
                {"type": "document", "source": {
                    "type": "base64", "media_type": "application/pdf",
                    "data": pdf_to_base64(pdf_path)}},
                {"type": "text", "text": "Elemzési feladat"}]},
            {"role": "assistant", "content": eredeti_valasz},
            {"role": "user", "content": javito}
        ])
    return "".join(b.text for b in valasz.content if hasattr(b, "text"))

def gemini_javitas(eredeti_valasz, kapott_n, pdf_path):
    javito = JAVITO_PROMPT_SABLON.format(kapott=kapott_n)
    valasz = gemini_model_obj.generate_content(
        f"Előző válaszod:\n\n{eredeti_valasz}\n\n{javito}",
        generation_config=genai.types.GenerationConfig(temperature=0.0))
    return valasz.text

# ═══════════════════════════════════════════════════════════
#  VÁLASZ ÉRTELMEZÉSE
# ═══════════════════════════════════════════════════════════

def tsv_blokkok_kinyerese(szoveg):
    blokkok = re.findall(r"```(?:[^\n]*)?\n(.*?)```", szoveg, re.DOTALL)
    eredmeny = []
    for blokk in blokkok:
        sorok = []
        for sor in blokk.strip().split("\n"):
            sor = sor.strip()
            if not sor: continue
            if "\t" in sor:
                sorok.append(sor.split("\t"))
            elif sor.count("|") > 2:
                cellak = [c.strip() for c in sor.strip("|").split("|")]
                if not all(re.match(r"^[-: ]+$", c) for c in cellak if c):
                    sorok.append(cellak)
        if sorok:
            eredmeny.append(sorok)
    return eredmeny

def valasz_ertelmezes(szoveg, fajlnev, modell, fut, pdf_path, javito_fn=None):
    """Értelmezi az AI választ. Ha nem 87 értékes, megpróbálja javíttatni."""
    d = {
        "fajlnev": fajlnev, "modell": modell, "fut": fut,
        "statusz": "BIZONYTALAN",
        "kizaras_ok": "", "kizaras_reszlet": "", "pico": "",
        "adat87": [], "adat_n": 0,
        "torzitas": [], "forras": [], "hatas": [],
        "manualis": False, "nyers": szoveg
    }

    if "⛔ ELIGIBILITÁS: KIZÁRANDÓ" in szoveg or "KIZÁRÁS OKA" in szoveg:
        d["statusz"] = "KIZÁRVA"
        m = re.search(r"KIZÁRÁS OKA:\s*(.+?)(?:\n\nRÉSZLETES|\n\nJAVASO|\Z)", szoveg, re.DOTALL)
        if m: d["kizaras_ok"] = m.group(1).strip()
        m2 = re.search(r"RÉSZLETES INDOKLÁS:\s*(.*?)(?:JAVASOLT TEENDŐ|ÉRINTETT PICO|\Z)", szoveg, re.DOTALL)
        if m2: d["kizaras_reszlet"] = m2.group(1).strip()[:500]
        m3 = re.search(r"ÉRINTETT PICO-ELEM:\s*(.+?)(?:\n|\Z)", szoveg)
        if m3: d["pico"] = m3.group(1).strip()
        return d

    if "✅ ELIGIBILITÁS: BEFOGADHATÓ" not in szoveg:
        return d

    d["statusz"] = "BEFOGADVA"
    if "MANUÁLIS ELLENŐRZÉS SZÜKSÉGES" in szoveg:
        d["manualis"] = True

    blokkok = tsv_blokkok_kinyerese(szoveg)

    # ── Adatkinyerés: 87-érték kényszerítés ──────────────────
    adat = blokkok[0][0] if blokkok and blokkok[0] else []
    
    if len(adat) != 87 and javito_fn and pdf_path:
        print(f"    ↺ {len(adat)} érték → javítás kérése...", end=" ", flush=True)
        for javitas_i in range(MAX_JAVITAS):
            try:
                javitott = javito_fn(szoveg, len(adat), pdf_path)
                jav_blokkok = tsv_blokkok_kinyerese(javitott)
                jav_adat = jav_blokkok[0][0] if jav_blokkok and jav_blokkok[0] else []
                if len(jav_adat) == 87:
                    adat = jav_adat
                    d["nyers"] = szoveg + "\n\n=== JAVÍTÁS ===\n" + javitott
                    print(f"✅ 87 érték ({javitas_i+1}. javítás)")
                    break
                else:
                    print(f"⚠️  még {len(jav_adat)}", end=" ", flush=True)
            except Exception as e:
                print(f"❌ javítás hiba: {str(e)[:40]}", end=" ", flush=True)
        else:
            print(f"→ marad {len(adat)}")

    d["adat_n"] = len(adat)
    d["adat87"] = adat

    # Torzítás, forrás, hatásméret
    if len(blokkok) > 1 and blokkok[1]: d["torzitas"] = blokkok[1][0]
    if len(blokkok) > 2 and blokkok[2]: d["forras"]   = blokkok[2][0]
    if len(blokkok) > 3 and blokkok[3]: d["hatas"]    = blokkok[3][0]

    return d

# ═══════════════════════════════════════════════════════════
#  TÖBBSÉGI SZAVAZÁS
# ═══════════════════════════════════════════════════════════

def normalizal(ertek):
    """Normalizálja az értéket összehasonlításhoz."""
    if ertek is None:
        return "nr"
    s = str(ertek).strip().lower()
    s = re.sub(r'\s+', ' ', s)
    # Szám: távolítsuk el a felesleges nullákat
    try:
        f = float(s)
        return str(round(f, 4))
    except:
        pass
    return s

def tobbsegi_szavazas(futtatások_adatok):
    """
    futtatások_adatok: list of list (minden futtatás 87 értéke)
    Visszatér:
      - vegyes_adat: list of 87 érték (a legtöbbször előforduló)
      - egyezesek: list of 87 int (hány futtatásban egyezik)
      - legjobb_idx: melyik futtatás nyert a legtöbb mezőn (szöveges mezőkhöz)
    """
    n_fut = len(futtatások_adatok)
    if n_fut == 0:
        return [], [], 0

    vegyes = []
    egyezesek = []
    pont_per_futtas = [0] * n_fut

    for i in range(87):
        tipus = MEZO_TIPUSOK.get(i, 'SZOV')
        ertekek = []
        for j, adat in enumerate(futtatások_adatok):
            v = adat[i] if i < len(adat) else "NR"
            ertekek.append(v)

        if tipus in ('SZAM', 'KAT'):
            # Normalizált értékek szavazása
            norm_ertekek = [normalizal(v) for v in ertekek]
            szamlalo = Counter(norm_ertekek)
            leggyakoribb_norm, db = szamlalo.most_common(1)[0]
            egyezesek.append(db)

            # Visszakeresés: az eredeti (nem normalizált) érték az első egyező futtatásból
            eredeti = next((ertekek[j] for j, nv in enumerate(norm_ertekek)
                           if nv == leggyakoribb_norm), ertekek[0])
            vegyes.append(eredeti)

            # Pontszám: amelyik futtatás a győztes értéket adta
            for j, nv in enumerate(norm_ertekek):
                if nv == leggyakoribb_norm:
                    pont_per_futtas[j] += 1
        else:
            # Szöveges mező: placeholder, a legjobb futtatásból töltjük ki
            vegyes.append(None)
            egyezesek.append(0)

    # Legjobb futtatás: amelyik a legtöbb számos/kat mezőn egyezett
    legjobb_idx = pont_per_futtas.index(max(pont_per_futtas))

    # Szöveges mezők: a legjobb futtatásból
    legjobb_adat = futtatások_adatok[legjobb_idx]
    for i in range(87):
        if vegyes[i] is None:
            vegyes[i] = legjobb_adat[i] if i < len(legjobb_adat) else "NR"
            # Egyezés szöveges mezőknél: hány futtatás adott nem-NR értéket
            nr_ertekek = [normalizal(futtatások_adatok[j][i] if i < len(futtatások_adatok[j]) else "NR")
                         for j in range(n_fut)]
            nem_nr = sum(1 for v in nr_ertekek if v not in ('nr', 'n/a', ''))
            egyezesek[i] = nem_nr

    return vegyes, egyezesek, legjobb_idx

def egyezes_fill(egyezes, n_fut):
    """Visszatér az egyezésnek megfelelő színnel."""
    if n_fut == 0: return None
    if egyezes >= ZOLD_KUSZOB:  return FILL_ZOLD
    if egyezes >= SARGA_KUSZOB: return FILL_SARGA
    return FILL_PIROS

# ═══════════════════════════════════════════════════════════
#  SABLON BETÖLTÉSE + EXTRA LAPOK
# ═══════════════════════════════════════════════════════════

def fejlec_sor(ws, sor, fejlecek, start_col=1, fill=FILL_FEJLEC):
    for i, f in enumerate(fejlecek):
        c = ws.cell(row=sor, column=start_col+i)
        c.value = f
        c.font = FEHER_FONT
        c.fill = fill
        c.alignment = Alignment(wrap_text=True)

def sablon_betolt():
    if not os.path.exists(SABLON_FAJL):
        raise FileNotFoundError(f"Sablon nem található: {SABLON_FAJL}")
    wb = openpyxl.load_workbook(SABLON_FAJL)

    # Összehasonlítás lap
    ws_ossz = wb.create_sheet("Összehasonlítás")
    fejlec_sor(ws_ossz, 1, [
        "Study ID","Fájlnév","Modell","Futtatás","Státusz",
        "Adat n","Manuális?","Javítás?","Összefoglalás (1. 200 kar.)"
    ])
    ws_ossz.freeze_panes = "A2"

    # Kizárt lap
    ws_kiz = wb.create_sheet("Kizárt")
    fejlec_sor(ws_kiz, 1, [
        "Study ID","Fájlnév","Modell","Futtatás",
        "Kizárás oka","Részletes indoklás","PICO-elem","Dátum"
    ], fill=FILL_PIROS)
    ws_kiz.freeze_panes = "A2"

    return wb

# ═══════════════════════════════════════════════════════════
#  SABLONBA ÍRÁS
# ═══════════════════════════════════════════════════════════

def id_to_de_sor(sid):   return int(sid) + 2
def id_to_bias_sor(sid): return int(sid) + 1
def id_to_szam_sorok(sid):
    o1 = (int(sid) - 1) * 2 + 2
    return o1, o1 + 1

def beir_ertekek(ws, sor, start_col, ertekek, fills=None, max_n=None):
    if max_n: ertekek = ertekek[:max_n]
    for i, v in enumerate(ertekek):
        c = ws.cell(row=sor, column=start_col + i)
        c.value = v if v not in (None, 'None', '') else None
        if fills and i < len(fills) and fills[i]:
            c.fill = fills[i]

def tanulmany_beir(wb, study_id, vegyes_adat, egyezesek, n_fut,
                   bias_d, hatas_adat, manualis, van_konfliktus):
    """Egy tanulmány végleges adatait beírja az összes sablonlapba."""

    fills = [egyezes_fill(e, n_fut) for e in egyezesek]

    # ── Data Extraction ───────────────────────────────────
    ws_de = wb['Data Extraction']
    de_sor = id_to_de_sor(study_id)
    beir_ertekek(ws_de, de_sor, 3, vegyes_adat[:87], fills[:87], max_n=87)

    # ── Bias lapok ────────────────────────────────────────
    if bias_d and bias_d.get('torzitas'):
        torzitas = bias_d['torzitas']
        bt = BIAS_TIPUS.get(len(torzitas))
        if bt and bt in wb.sheetnames:
            beir_ertekek(wb[bt], id_to_bias_sor(study_id), 3, torzitas)

    # ── Source Bias ───────────────────────────────────────
    if bias_d and bias_d.get('forras'):
        beir_ertekek(wb['Source Bias'], id_to_bias_sor(study_id), 3,
                     bias_d['forras'][:8], max_n=8)

    # ── Számítások ────────────────────────────────────────
    ws_sz = wb['Számítások']
    o1_sor, o2_sor = id_to_szam_sorok(study_id)
    if hatas_adat:
        beir_ertekek(ws_sz, o1_sor, 4, hatas_adat[:12], max_n=12)
        if len(hatas_adat) >= 13:
            beir_ertekek(ws_sz, o2_sor, 4, hatas_adat[12:24], max_n=12)
        else:
            ws_sz.cell(o2_sor, 4).value = "— kézi kitöltés —"
            ws_sz.cell(o2_sor, 4).fill = FILL_SARGA

    # ── Megjegyzések ──────────────────────────────────────
    ws_meg = wb['Megjegyzések']
    meg_sor = id_to_bias_sor(study_id)

    megjegyzesek = []
    if manualis:
        megjegyzesek.append("AI manuális ellenőrzés jelzés (Step 0b)")
    if van_konfliktus:
        megjegyzesek.append("STÁTUSZ-KONFLIKTUS: Claude és Gemini eltérő befogadási döntés")

    # Alacsony megbízhatóságú mezők (piros)
    piros_mezok = [i+1 for i, e in enumerate(egyezesek) if e < SARGA_KUSZOB and n_fut > 0]
    if piros_mezok:
        megjegyzesek.append(f"Alacsony egyezés (≤2/{n_fut}): mező #{', #'.join(str(m) for m in piros_mezok[:10])}"
                           + (" ..." if len(piros_mezok) > 10 else ""))

    ws_meg.cell(meg_sor, 3).value = "; ".join(megjegyzesek) if megjegyzesek else None
    ws_meg.cell(meg_sor, 6).value = "Igen" if megjegyzesek else "Nem"
    if megjegyzesek:
        ws_meg.cell(meg_sor, 6).fill = FILL_NARANCS if van_konfliktus else FILL_SARGA


def osszhasonlito_sorok(wb, study_id, fajlnev, futtatások, van_konfliktus):
    ws_ossz = wb['Összehasonlítás']
    for d in futtatások:
        adat = d.get('adat87') or []
        osszefoglalas = (adat[0][:200] if adat
                        else "KIZÁRVA: " + d.get('kizaras_ok','')[:150])
        javitas = "Igen" if '=== JAVÍTÁS ===' in d.get('nyers','') else "Nem"
        ws_ossz.append([
            study_id, fajlnev, d['modell'], d['fut'], d['statusz'],
            d.get('adat_n', 0),
            "IGEN ⚠️" if d.get('manualis') else "Nem",
            javitas, osszefoglalas
        ])
        r = ws_ossz.max_row
        f_map = {'BEFOGADVA': FILL_ZOLD, 'KIZÁRVA': FILL_PIROS}
        ws_ossz.cell(r, 5).fill = f_map.get(d['statusz'], FILL_SARGA)
        if van_konfliktus: ws_ossz.cell(r, 8).fill = FILL_NARANCS
    ws_ossz.append([""] * 9)


def kizart_beir(wb, study_id, fajlnev, kiz_futtatások):
    ws_kiz = wb['Kizárt']
    latott = set()
    for d in kiz_futtatások:
        key = d['kizaras_ok'][:40]
        if key not in latott:
            ws_kiz.append([
                study_id, fajlnev, d['modell'], d['fut'],
                d['kizaras_ok'], d['kizaras_reszlet'],
                d['pico'], datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
            ])
            ws_kiz.cell(ws_kiz.max_row, 5).fill = FILL_PIROS
            latott.add(key)

# ═══════════════════════════════════════════════════════════
#  SEGÉDFÜGGVÉNYEK
# ═══════════════════════════════════════════════════════════

def fajlnev_to_id(fn):
    m = re.match(r'^(\d+)', str(fn))
    return int(m.group(1)) if m else None

def mar_kesz(nyers_mappa):
    kesz = set()
    if not os.path.exists(nyers_mappa): return kesz
    for f in os.listdir(nyers_mappa):
        if f.endswith("_HIBA.txt") or not f.endswith(".txt"): continue
        m = re.match(r'^(.+?)_(Claude|Gemini)_fut(\d+)\.txt$', f)
        if m: kesz.add((m.group(1), m.group(2), int(m.group(3))))
    return kesz

def excel_mentes(wb, vegleges=False):
    nev = (f"Waldorf_Adatkinyeres_{datetime.datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
           if vegleges else "Waldorf_Adatkinyeres_FOLYAMATBAN.xlsx")
    wb.save(nev)
    return nev

# ═══════════════════════════════════════════════════════════
#  FŐ PROGRAM
# ═══════════════════════════════════════════════════════════

def main():
    print("=" * 65)
    print("  WALDORF ADATKINYERŐ v4 — Többségi szavazás + 87-kényszer")
    print("=" * 65)

    for f in [PROMPT_FAJL, SABLON_FAJL]:
        if not os.path.exists(f):
            print(f"❌ '{f}' nem található!"); return

    if not os.path.exists(PDF_MAPPA):
        print(f"❌ '{PDF_MAPPA}' mappa nem található!"); return

    with open(PROMPT_FAJL, "r", encoding="utf-8") as f:
        prompt = f.read()
    print(f"✅ Prompt: {len(prompt):,} karakter")

    pdf_fajlok = sorted([f for f in os.listdir(PDF_MAPPA) if f.lower().endswith(".pdf")])
    if not pdf_fajlok:
        print("❌ Nincs PDF!"); return
    print(f"✅ {len(pdf_fajlok)} PDF fájl")

    NYERS = "nyers_valaszok"
    os.makedirs(NYERS, exist_ok=True)

    kesz_set = mar_kesz(NYERS)
    if kesz_set:
        print(f"ℹ️  {len(kesz_set)} kész futtatás betöltve\n")

    wb = sablon_betolt()
    print(f"✅ Sablon betöltve: {SABLON_FAJL}\n")

    modellek = []
    if CLAUDE_AKTIV:  modellek.append(("Claude", claude_hivas, claude_javitas))
    if GEMINI_AKTIV:  modellek.append(("Gemini", gemini_hivas, gemini_javitas))
    if not modellek:
        print("❌ Sem Claude, sem Gemini!"); return

    bef_db = kiz_db = hiba_db = 0
    statisztika = []  # (fajlnev, n_fut_bef, atlag_egyezes, n_piros)

    for idx, fajlnev in enumerate(pdf_fajlok, 1):
        pdf_path = os.path.join(PDF_MAPPA, fajlnev)
        alap = fajlnev[:-4]
        study_id = fajlnev_to_id(fajlnev)

        print(f"\n{'─'*65}")
        print(f"[{idx}/{len(pdf_fajlok)}] {fajlnev}")
        print(f"{'─'*65}")

        futtatások = []

        for modell_nev, api_fn, javito_fn in modellek:
            for fut in range(1, FUTTATÁSOK_SZAMA + 1):
                nyers_fajl = os.path.join(NYERS, f"{alap}_{modell_nev}_fut{fut}.txt")

                if (alap, modell_nev, fut) in kesz_set:
                    try:
                        with open(nyers_fajl, "r", encoding="utf-8") as f_:
                            nyers = f_.read()
                        # Betöltöttaknál nem javítunk újra
                        d = valasz_ertelmezes(nyers, fajlnev, modell_nev, fut, None, None)
                        futtatások.append(d)
                        extra = " ⚠️" if d.get('manualis') else ""
                        print(f"  [{modell_nev}] {fut}. ⏭️  {d['statusz']} (n={d.get('adat_n',0)}){extra}")
                        continue
                    except:
                        pass

                print(f"  [{modell_nev}] {fut}. futtatás...", end=" ", flush=True)
                try:
                    nyers = api_ujraprobalkozassal(api_fn, prompt, pdf_path)
                    d = valasz_ertelmezes(nyers, fajlnev, modell_nev, fut,
                                         pdf_path, javito_fn)
                    # Mentés (javított választ is tartalmazza ha volt)
                    with open(nyers_fajl, "w", encoding="utf-8") as nf:
                        nf.write(d['nyers'])
                    futtatások.append(d)
                    extra = " ⚠️ manuális" if d.get('manualis') else ""
                    if d['statusz'] == 'BEFOGADVA':
                        print(f"✅ BEFOGADVA (n={d.get('adat_n',0)}){extra}")
                    elif d['statusz'] == 'KIZÁRVA':
                        print(f"⛔ KIZÁRVA — {d['kizaras_ok'][:50]}")
                    else:
                        print(f"⚠️  BIZONYTALAN")
                except Exception as e:
                    print(f"❌ HIBA: {str(e)[:80]}")
                    with open(os.path.join(NYERS, f"{alap}_{modell_nev}_fut{fut}_HIBA.txt"),
                              "w", encoding="utf-8") as hf:
                        hf.write(traceback.format_exc())
                    hiba_db += 1

        if not study_id:
            print("  ⚠️  Study ID nem olvasható, kihagyva"); continue

        bef_futtatások = [d for d in futtatások if d['statusz'] == 'BEFOGADVA']
        kiz_futtatások = [d for d in futtatások if d['statusz'] == 'KIZÁRVA']
        van_konfliktus = (len(bef_futtatások) > 0 and len(kiz_futtatások) > 0)
        manualis = any(d.get('manualis') for d in bef_futtatások)

        if bef_futtatások:
            # Többségi szavazás az összes 87-értékes futtatás között
            adatok_87 = [d['adat87'] for d in bef_futtatások if d['adat_n'] == 87]
            if not adatok_87:
                # Ha nincs 87-es, vesszük ami van (legjobb közelítő)
                adatok_87 = [d['adat87'] for d in bef_futtatások if d['adat87']]

            if adatok_87:
                vegyes_adat, egyezesek, legjobb_idx = tobbsegi_szavazas(adatok_87)
                n_fut = len(adatok_87)
                n_piros = sum(1 for e in egyezesek if e < SARGA_KUSZOB)
                atlag_e = sum(egyezesek) / len(egyezesek) if egyezesek else 0
                statisztika.append((fajlnev, n_fut, atlag_e, n_piros))

                print(f"  📊 Szavazás: {n_fut} futtatás | átlag egyezés: {atlag_e:.1f}/{n_fut} | piros cellák: {n_piros}/87")

                # Bias a legjobb futtatásból
                bias_d = bef_futtatások[legjobb_idx] if legjobb_idx < len(bef_futtatások) else bef_futtatások[0]
                hatas = bias_d.get('hatas', [])

                tanulmany_beir(wb, study_id, vegyes_adat, egyezesek, n_fut,
                               bias_d, hatas, manualis, van_konfliktus)
                bef_db += 1

        if kiz_futtatások:
            kizart_beir(wb, study_id, fajlnev, kiz_futtatások)
            if not bef_futtatások:
                kiz_db += 1

        osszhasonlito_sorok(wb, study_id, fajlnev, futtatások, van_konfliktus)

        nev = excel_mentes(wb, vegleges=False)
        print(f"  💾 → {nev}")

    # ── Végső mentés ──────────────────────────────────────
    nev = excel_mentes(wb, vegleges=True)

    # ── Összefoglalás ─────────────────────────────────────
    print(f"\n{'='*65}")
    print("  KÉSZ!")
    print(f"{'='*65}")
    print(f"  ✅ Befogadott: {bef_db}")
    print(f"  ⛔ Kizárt:     {kiz_db}")
    print(f"  ❌ API hibák:  {hiba_db}")

    if statisztika:
        atlag_ossz = sum(s[2] for s in statisztika) / len(statisztika)
        print(f"\n  📊 Átlagos egyezési arány: {atlag_ossz:.2f}/futtatás")
        print(f"  🔴 Legtöbb piros cella:")
        for fn, nf, ae, np in sorted(statisztika, key=lambda x: -x[3])[:5]:
            print(f"     {fn}: {np} piros cella ({ae:.1f}/{nf} átlag)")

    if gemini_sikertelen_pdf:
        print(f"\n{'='*65}")
        print("  ⚠️  GEMINI NEM TUDTA OLVASNI (szkennelt PDF):")
        print(f"{'='*65}")
        for fn in gemini_sikertelen_pdf:
            print(f"  📄 {fn}  ← OCR-rel szöveges PDF-fé kell alakítani!")
        print()
        print("  Megoldás: Adobe Acrobat / smallpdf.com / ilovepdf.com")
        print("  → 'Szöveg felismerése' / 'OCR' funkció")
        print("  → Mentés szöveges PDF-ként, felülírva az eredetit")

    print(f"\n  📊 Kimenet: {nev}")
    print(f"  📁 Nyers válaszok: nyers_valaszok/")
    print(f"{'='*65}\n")


if __name__ == "__main__":
    main()
